package org.bhf.json;

/**
 * Copyright (c) 2007, Blue Hole Software. All rights reserved.
 * Code licensed under the Apache 2.0 License:
 * http://www.apache.org/licenses/
 */

import java.io.IOException;
import java.io.Writer;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.Date;

/**
 * Collection of built-in writer
 */
public final class NativeJSONWriters
{
    /**
     * Write a built-in class as a JSON value
     *
     * @param bool May be <code>null</code>
     * @param out Must not be <code>null</code>
     * @param writer Not used. Just for signature match.
     * @throws IOException Write error
     * @exclude
     */
    public static void write( final Boolean bool, final Writer out, final JSONWriter writer )
        throws IOException
    {
        if( bool == null ) writeNull( out ); else
        write( bool.booleanValue(), out, writer );
    }

    /**
     * Write a built-in class as a JSON value
     *
     * @param c May be <code>null</code>
     * @param out Must not be <code>null</code>
     * @param writer Not used. Just for signature match.
     * @throws IOException Write error
     * @exclude
     */
    public static void write( final Character c, final Writer out, final JSONWriter writer )
        throws IOException
    {
        if( c == null ) writeNull( out ); else
        write( c.charValue(), out, writer );
    }

    /**
     * Write a built-in class as a JSON value
     *
     * @param b May be <code>null</code>
     * @param out Must not be <code>null</code>
     * @param writer Not used. Just for signature match.
     * @throws IOException Write error
     * @exclude
     */
    public static void write( final Byte b, final Writer out, final JSONWriter writer )
        throws IOException
    {
        if( b == null ) writeNull( out ); else
        write( b.byteValue(), out, writer );
    }

    /**
     * Write a built-in class as a JSON value
     *
     * @param s May be <code>null</code>
     * @param out Must not be <code>null</code>
     * @param writer Not used. Just for signature match.
     * @throws IOException Write error
     * @exclude
     */
    public static void write( final Short s, final Writer out, final JSONWriter writer )
        throws IOException
    {
        if( s == null ) writeNull( out ); else
        write( s.shortValue(), out, writer );
    }

    /**
     * Write a built-in class as a JSON value
     *
     * @param i May be <code>null</code>
     * @param out Must not be <code>null</code>
     * @param writer Not used. Just for signature match.
     * @throws IOException Write error
     * @exclude
     */
    public static void write( final Integer i, final Writer out, final JSONWriter writer )
        throws IOException
    {
        if( i == null ) writeNull( out ); else
        write( i.intValue(), out, writer );
    }

    /**
     * Write a built-in class as a JSON value
     *
     * @param l May be <code>null</code>
     * @param out Must not be <code>null</code>
     * @param writer Not used. Just for signature match.
     * @throws IOException Write error
     * @exclude
     */
    public static void write( final Long l, final Writer out, final JSONWriter writer )
        throws IOException
    {
        if( l == null ) writeNull( out ); else
        write( l.longValue(), out, writer );
    }

    /**
     * Write a built-in class as a JSON value
     *
     * @param f May be <code>null</code>
     * @param out Must not be <code>null</code>
     * @param writer Not used. Just for signature match.
     * @throws IOException Write error
     * @exclude
     */
    public static void write( final Float f, final Writer out, final JSONWriter writer )
        throws IOException
    {
        if( f == null ) writeNull( out ); else
        write( f.floatValue(), out, writer );
    }

    /**
     * Write a built-in class as a JSON value
     *
     * @param d May be <code>null</code>
     * @param out Must not be <code>null</code>
     * @param writer Not used. Just for signature match.
     * @throws IOException Write error
     * @exclude
     */
    public static void write( final Double d, final Writer out, final JSONWriter writer )
        throws IOException
    {
        if( d == null ) writeNull( out ); else
        write( d.doubleValue(), out, writer );
    }

    /**
     * Write a built-in class as a JSON value
     *
     * @param bool value
     * @param out Must not be <code>null</code>
     * @param writer Not used. Just for signature match.
     * @throws IOException Write error
     * @exclude
     */
    public static void write( final boolean bool, final Writer out, final JSONWriter writer )
        throws IOException
    {
        out.write( bool ? "true" : "false" );
    }

    /**
     * Write a built-in class as a JSON value
     *
     * @param c value
     * @param out Must not be <code>null</code>
     * @param writer Not used. Just for signature match.
     * @throws IOException Write error
     * @exclude
     */
    public static void write( final char c, final Writer out, final JSONWriter writer )
        throws IOException
    {
        write( String.valueOf( c ), out, writer );
    }

    /**
     * Write a built-in class as a JSON value
     *
     * @param b value
     * @param out Must not be <code>null</code>
     * @param writer Not used. Just for signature match.
     * @throws IOException Write error
     * @exclude
     */
    public static void write( final byte b, final Writer out, final JSONWriter writer )
        throws IOException
    {
        write( (long)b, out, writer );
    }

    /**
     * Write a built-in class as a JSON value
     *
     * @param s value
     * @param out Must not be <code>null</code>
     * @param writer Not used. Just for signature match.
     * @throws IOException Write error
     * @exclude
     */
    public static void write( final short s, final Writer out, final JSONWriter writer )
        throws IOException
    {
        write( (long)s, out, writer );
    }

    /**
     * Write a built-in class as a JSON value
     *
     * @param i value
     * @param out Must not be <code>null</code>
     * @param writer Not used. Just for signature match.
     * @throws IOException Write error
     * @exclude
     */
    public static void write( final int i, final Writer out, final JSONWriter writer )
        throws IOException
    {
        write( (long)i, out, writer );
    }

    /**
     * Write a built-in class as a JSON value
     *
     * @param l value
     * @param out Must not be <code>null</code>
     * @throws IOException Write error
     * @exclude
     */
    public static void write( final long l, final Writer out, final JSONWriter writer )
        throws IOException
    {
        out.write( Long.toString( l ) );
    }

    /**
     * Write a built-in class as a JSON value
     *
     * @param f value
     * @param out Must not be <code>null</code>
     * @param writer Not used. Just for signature match.
     * @throws IOException Write error
     * @exclude
     */
    public static void write( final float f, final Writer out, final JSONWriter writer )
        throws IOException
    {
        write( (double)f, out, writer );
    }

    /**
     * Write a built-in class as a JSON value
     *
     * @param d value
     * @param out Must not be <code>null</code>
     * @param writer Not used. Just for signature match.
     * @throws IOException Write error
     * @exclude
     */
    public static void write( final double d, final Writer out, final JSONWriter writer )
        throws IOException
    {
        out.write( Double.toString( d ) );
    }

    /**
     * Write a built-in class as a JSON value
     *
     * @param d value
     * @param out Must not be <code>null</code>
     * @param writer Not used. Just for signature match.
     * @throws IOException Write error
     * @exclude
     */
    public static void write( final Date d, final Writer out, final JSONWriter writer )
        throws IOException
    {
        write( "@" + d.getTime() + "@", out, writer );
    }

    /**
     * Write a null JSON value
     *
     * @param out Must not be <code>null</code>
     * @throws IOException Write error
     * @exclude
     */
    public static void writeNull( final Writer out )
        throws IOException
    {
        out.write( "null" );
    }

    //
    // Non-primitive Built-in Objects
    //

    /**
     * Write a <code>String</code> as a properly escaped and quoted JSON value
     *
     * @param string May be <code>null</code>
     * @param out Must not be <code>null</code>
     * @param writer Not used. Just for signature match.
     * @throws IOException Write error
     * @exclude
     */
    public static void write( final String string, final Writer out, final JSONWriter writer )
        throws IOException
    {
        if( string == null ) { writeNull( out ); return; }
        
        out.write( Util.escape( string ) );
    }

    /**
     * Write the string representation of an enumeration.
     *
     * @param enumeration May be <code>null</code>
     * @param out Must not be <code>null</code>
     * @param writer Not used. Just for signature match.
     * @throws IOException Write error
     * @exclude
     */
    public static void     write( final Enum enumeration, final Writer out, final JSONWriter writer )
        throws IOException
    {
        if( enumeration == null ) { writeNull( out ); return; }
        write( enumeration.toString(), out, writer );
    }

    //
    // Package
    //

    /**
     * Return <code>true</code> if we have built-in support for the given <code>Type</code>. In other
     * words, for built-in types we do not have to generate a custom writer and can use the write
     * methods directly.
     *
     * @param type Must not be <code>null</code>
     * @return <code>true</code> if we have built-in support for the given <code>Type</code>.
     */
    static boolean  hasBuiltInSupport( final Type type )
    {
        return getBuiltInMethod( type ) != null;
    }

    /**
     * Return <code>true</code> if we have built-in support for the given <code>Type</code>. In other
     * words, for built-in types we do not have to generate a custom writer and can use the write
     * methods directly.
     *
     * @param type Must not be null
     * @return Reader method or null.
     */
    static Method   getBuiltInMethod( final Type type )
    {
        return getWriterMethod( NativeJSONWriters.class, type );
    }

    //
    // Private
    //

    private static Method   getWriterMethod( final Class writerClass, final Type type )
    {
        try
        {
            final Class clazz = Util.digRawType( type );

            if( Enum.class.isAssignableFrom( clazz ) )
                return writerClass.getMethod( "write", Enum.class, Writer.class, JSONWriter.class );
            else
                return writerClass.getMethod( "write", clazz, Writer.class, JSONWriter.class );
        }
        catch( NoSuchMethodException e )
        {
            return null;
        }
        catch( UnsupportedTypeException e )
        {
            return null;
        }
    }
}